{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 感知机（Perceptron）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import random\n",
    "from matplotlib import pyplot as plt\n",
    "import time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Perceptron:\n",
    "    \"\"\"\n",
    "    感知机\n",
    "    使用 fit(Xtrain, Ytrain) 训练数据\n",
    "    使用 predict(Xtest) 对测试集进行预测\n",
    "    使用 getw() 获取估计参数向量\n",
    "    使用 getDimen() 获取向量空间维数\n",
    "    使用 drawTwoDimenPic() 对二维的向量空间画出训练集数据、测试集数据和分割面的图片\n",
    "    \"\"\"\n",
    "    # 最大迭代次数\n",
    "    iteration = 10000\n",
    "    # 随机种子，决定迭代起始样本\n",
    "    randomstate = 0\n",
    "    \n",
    "    def __init__(self, iteration=10000, randomstate=0):\n",
    "        '''\n",
    "        初始化感知机\n",
    "        iteration：最大迭代次数\n",
    "        randomstate：随机种子\n",
    "        '''\n",
    "        self.iteration = iteration\n",
    "        self.randomstate = randomstate\n",
    "        \n",
    "        \n",
    "    def __sign(self, inputdata):\n",
    "        '''\n",
    "        二值函数\n",
    "        inputdata：二值函数输入变量\n",
    "        返回值：二值函数输出变量\n",
    "        '''\n",
    "        if inputdata > 0:\n",
    "            return 1\n",
    "        elif inputdata < 0:\n",
    "            return -1\n",
    "        else:\n",
    "            return 0\n",
    "        \n",
    "        \n",
    "    def fit(self, Xtrain, Ytrain, learningrate=0.5, fastmode=False, useglobal=True):\n",
    "        '''\n",
    "        训练测试样本\n",
    "        Xtrain：训练样本的数据，N * d 矩阵，第 i 行表示第 i 个数据\n",
    "        Ytrain：训练样本的标签，N * 1 矩阵，第 i 行表示第 i 个标签\n",
    "        返回值：(实际迭代的次数, 训练样本测试准确率)\n",
    "        '''\n",
    "        N, D = Xtrain.shape  # N：训练样本的个数；D：训练样本的维数\n",
    "        \n",
    "        # 获取迭代起始样本的序号\n",
    "        random.seed(self.randomstate)\n",
    "        start_index = int(random.random() * N)\n",
    "        \n",
    "        # 初始化估计参数\n",
    "        W = Xtrain[start_index, :]\n",
    "        w_0 = 0\n",
    "        cor_num = 0  # 分类正确的样本数量\n",
    "        \n",
    "        # 用来迭代估计参数的容器\n",
    "        new_W = W\n",
    "        new_w_0 = w_0\n",
    "        # 分类错误的数据样本及其标签，用来更新 W 和 w_0\n",
    "        mis_x, mis_y = (Xtrain[start_index - 1,:], Ytrain[start_index - 1])\n",
    "        # 结束迭代循环的钥匙\n",
    "        key = False\n",
    "        \n",
    "        # 开始迭代，最大迭代次数为 iteration\n",
    "        for cycle in range(self.iteration):\n",
    "            # 使用分类错误样本更新 W 和 w_0\n",
    "            new_W = new_W + mis_y * mis_x * learningrate\n",
    "            new_w_0 = new_w_0 + mis_y * 1 * learningrate\n",
    "            while fastmode == True and self.__sign(np.matmul(mis_x, np.transpose(new_W)) + new_w_0) != mis_y:\n",
    "                new_W = new_W + mis_y * mis_x * learningrate\n",
    "                new_w_0 = new_w_0 + mis_y * 1 * learningrate\n",
    "            \n",
    "            # 累计更新后的估计参数向量分类正确的样本数量\n",
    "            calc = 0\n",
    "            key = True\n",
    "            \n",
    "            for i in range(- start_index, N - start_index):\n",
    "                x = Xtrain[i, :]  # 第 i 个数据\n",
    "                y = Ytrain[i]  # 第 i 个数据对应的标签\n",
    "                \n",
    "                # 使用估计参数 W 和 w_0 对数据 x 进行预测，得到结果 y_predict\n",
    "                y_predict = self.__sign(np.matmul(x, np.transpose(new_W)) + new_w_0) \n",
    "                if y_predict == y:\n",
    "                    calc += 1\n",
    "                else:\n",
    "                    # 记录分类错误的样本数据及其标签\n",
    "                    mis_x = x\n",
    "                    mis_y = y\n",
    "                    key = False\n",
    "                    if useglobal == False:\n",
    "                        break\n",
    "                    \n",
    "            if useglobal == False or calc >= cor_num:\n",
    "                # 获得更优的估计参数向量\n",
    "                W = new_W\n",
    "                w_0 = new_w_0\n",
    "                cor_num = calc\n",
    "            if key == True:\n",
    "                # 得到最优估计参数向量\n",
    "                break\n",
    "        \n",
    "        # 记录感知机最后计算得到的结果\n",
    "        self.W = W\n",
    "        self.w_0 = w_0\n",
    "        self.dimen = D\n",
    "        self.Xtrain = Xtrain\n",
    "        self.Ytrain = Ytrain\n",
    "        \n",
    "        return (cycle + 1, cor_num / N)\n",
    "        \n",
    "        \n",
    "    def predict(self, Xtest):\n",
    "        '''\n",
    "        对测试样本进行预测\n",
    "        Xtest：测试样本的数据，N * d 矩阵，第 i 行表示第 i 个数据\n",
    "        返回值：测试样本的预测结果，N * 1 矩阵，第 i 行表示第 i 个标签\n",
    "        '''\n",
    "        N, D = Xtest.shape  # N：测试样本的数量；D：测试样本的维数\n",
    "        \n",
    "        # 获取感知机训练得到的参数\n",
    "        try:\n",
    "            W = self.W\n",
    "            w_0 = self.w_0\n",
    "            dimen = self.dimen\n",
    "        except:\n",
    "            raise Exception(\"You should use Perceptron.fit() to train your data first!\")\n",
    "        \n",
    "        # 检测训练样本和测试样本是否为同一维度\n",
    "        if D != dimen:\n",
    "            raise Exception(\"The dimensionality of Test data is different from Train's!\")\n",
    "        \n",
    "        # 对测试集进行预测\n",
    "        y_predict = np.zeros(N, np.int8)\n",
    "        for i in range(N):\n",
    "            x = Xtest[i, :]  # 测试集第 i 个数据\n",
    "            y_predict[i] = self.__sign(np.matmul(x, np.transpose(W)) + w_0)\n",
    "        \n",
    "        # 保存测试数据集和测试结果集\n",
    "        self.Xtest = Xtest\n",
    "        self.Ypred = y_predict\n",
    "        \n",
    "        return y_predict\n",
    "        \n",
    "        \n",
    "    def getw(self):\n",
    "        '''\n",
    "        获取估计参数向量\n",
    "        返回值：(估计参数向量W, w_0)  或者  None(感知机未训练样本)\n",
    "        '''\n",
    "        try:\n",
    "            return (self.w_0, self.W)\n",
    "        except:\n",
    "            return None\n",
    "        \n",
    "        \n",
    "    def getDimen(self):\n",
    "        '''\n",
    "        获取感知机训练样本的维数\n",
    "        返回值：训练样本的维数\n",
    "        '''\n",
    "        try:\n",
    "            return self.dimen\n",
    "        except:\n",
    "            return None\n",
    "        \n",
    "        \n",
    "    def drawTwoDimenPic(self, traindata=True, testdata=False, line=True, color=['r','b','y','g','k']):\n",
    "        '''\n",
    "        画出包含训练集、测试集和分类面的二维图片，只适用于训练集为二维的情况\n",
    "        traindata：是否包含训练集数据\n",
    "        testdata：是否包含测试集数据\n",
    "        line：是否包含分类面\n",
    "        返回值：图像\n",
    "        '''\n",
    "        # 检测条件\n",
    "        try:\n",
    "            dimen = self.dimen\n",
    "            W = self.W\n",
    "            w_0 = self.w_0\n",
    "            Xtrain = self.Xtrain\n",
    "            Ytrain = self.Ytrain\n",
    "        except:\n",
    "            raise Exception(\"You should use Perceptron.fit() to train your data first!\")\n",
    "            \n",
    "        if testdata == True:\n",
    "            try:\n",
    "                Xtest = self.Xtest\n",
    "                Ypred = self.Ypred\n",
    "            except:\n",
    "                raise Exception(\"If you want to show Test data, \" \n",
    "                                + \"you should use Perceptron.predict() to test your data first!\")\n",
    "                \n",
    "        if dimen != 2:\n",
    "            raise Exception(\"This method can use only when the dimensionality of Xtrain is 2!\")\n",
    "            \n",
    "        # 显示需要显示的训练集数据\n",
    "        if traindata == True:\n",
    "            train_pos_x1 = np.array([])\n",
    "            train_pos_x2 = np.array([])\n",
    "            train_neg_x1 = np.array([])\n",
    "            train_neg_x2 = np.array([])\n",
    "            for i in range(len(Ytrain)):\n",
    "                if Ytrain[i] > 0:\n",
    "                    train_pos_x1 = np.append(train_pos_x1, np.array([Xtrain[i][0]]))\n",
    "                    train_pos_x2 = np.append(train_pos_x2, np.array([Xtrain[i][1]]))\n",
    "                elif Ytrain[i] < 0:\n",
    "                    train_neg_x1 = np.append(train_neg_x1, np.array([Xtrain[i][0]]))\n",
    "                    train_neg_x2 = np.append(train_neg_x2, np.array([Xtrain[i][1]]))\n",
    "            plt.plot(train_pos_x1, train_pos_x2, '.' + color[0])\n",
    "            plt.plot(train_neg_x1, train_neg_x2, '.' + color[1])\n",
    "                \n",
    "        # 显示需要显示的测试集数据\n",
    "        if testdata == True:\n",
    "            test_pos_x1 = np.array([])\n",
    "            test_pos_x2 = np.array([])\n",
    "            test_neg_x1 = np.array([])\n",
    "            test_neg_x2 = np.array([])\n",
    "            for i in range(len(Ypred)):\n",
    "                if Ypred[i] > 0:\n",
    "                    test_pos_x1 = np.append(test_pos_x1, np.array([Xtest[i][0]]))\n",
    "                    test_pos_x2 = np.append(test_pos_x2, np.array([Xtest[i][1]]))\n",
    "                elif Ypred[i] < 0:\n",
    "                    test_neg_x1 = np.append(test_neg_x1, np.array([Xtest[i][0]]))\n",
    "                    test_neg_x2 = np.append(test_neg_x2, np.array([Xtest[i][1]]))\n",
    "            plt.plot(test_pos_x1, test_pos_x2, '.' + color[2])\n",
    "            plt.plot(test_neg_x1, test_neg_x2, '.' + color[3])\n",
    "            \n",
    "        # 显示分割面\n",
    "        if line == True:\n",
    "            if W[0] == 0 and W[1] == 0 and w_0 == 0:\n",
    "                raise Exception(\"Error! W = [0, 0] and w_0 = 0\")\n",
    "            elif W[0] == 0:\n",
    "                # 与x轴平行分来面\n",
    "                line_x = np.linspace(Xtrain[:,0].min(), Xtrain[:,0].max())\n",
    "                line_y = np.ones(line_x.shape) * w_0 / (- W[1])\n",
    "            elif W[1] == 0:\n",
    "                # 与y轴平行分类面\n",
    "                line_y = np.linspace(Xtrain[:,1].min(), Xtrain[:,1].man())\n",
    "                line_x = np.ones(line_y.shape) * w_0 / (- W[0])\n",
    "            else:\n",
    "                # 普通分类面\n",
    "                x_min = Xtrain[:,0].min()\n",
    "                x_max = Xtrain[:,0].max()\n",
    "                y_min = Xtrain[:,1].min()\n",
    "                y_max = Xtrain[:,1].max()\n",
    "                if np.abs((x_max - x_min) * W[0] / (- W[1])) < np.abs(y_max - y_min):\n",
    "                    line_x = np.linspace(x_min, x_max)\n",
    "                    line_y = line_x * W[0] / (- W[1]) + w_0 / (- W[1])\n",
    "                else:\n",
    "                    line_y = np.linspace(y_min, y_max)\n",
    "                    line_x = line_y * W[1] / (- W[0]) + w_0 / (- W[0])\n",
    "            plt.plot(line_x, line_y, '-' + color[4])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "迭代次数 = 8\n",
      "正确率   = 1.0\n",
      "预测结果 = [ 1 -1 -1 -1]\n",
      "耗费时间 = 0.0020012855529785156\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "X_train = np.array(\n",
    "    [[1,2],[5,7],[1,1],[8,5],[4,1],\n",
    "     [3,1],[4,9],[3,10],[3,2],[2,1],\n",
    "     [8,8],[7,9],[2,0],[0,0],[6,11],\n",
    "     [2,9],[4,2],[3,9],[6,0],[6,9]]\n",
    ")\n",
    "Y_train = np.array(\n",
    "    [1,-1,1,-1,1,\n",
    "     1,-1,-1,1,1,\n",
    "     -1,-1,1,1,-1,\n",
    "     -1,1,-1,1,-1]\n",
    ")\n",
    "X_test = np.array([[0.5,0.5],[4,8],[3,10],[5,6]])\n",
    "\n",
    "start_time = time.time()\n",
    "\n",
    "perce = Perceptron()\n",
    "n, score = perce.fit(X_train, Y_train, learningrate = 1, fastmode = True, useglobal=True)\n",
    "print(\"迭代次数 = \" + str(n))\n",
    "print(\"正确率   = \" + str(score))\n",
    "\n",
    "y_pred = perce.predict(X_test)\n",
    "print(\"预测结果 = \" + str(y_pred))\n",
    "\n",
    "end_time = time.time()\n",
    "print(\"耗费时间 = \" + str(end_time - start_time))\n",
    "\n",
    "perce.drawTwoDimenPic(testdata=True)\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
